#include "exp_data.h"
#include "cvxImage.h"
#include <conio.h>
#include <algorithm>

using namespace std;

ExpItem::ExpItem()
{
	m_isSquare = false;
	m_isCaustic  = false;
	m_type = 0;
}

ExpItem::~ExpItem()
{

}

static int randRange(int s, int e)
{
	assert(s <= e);
	if (s == e)
	{
		return e;
	}
	return s + rand()%(e-s);
}

void ExpItem::random()
{
	for (int i = 0; i<m_pts.size(); ++i)
	{
		int j = randRange(i, m_pts.size()-1);
		std::swap(m_pts[i], m_pts[j]);
		std::swap(m_gt[i], m_gt[j]);
		//_cprintf("i = %d, j = %d\n", i, j);
	}
}

bool ExpItem::checkResult()
{
//	return m_pvResult.size() == m_pts.size();
	bool isOk = m_pvResult.size() == m_pts.size();
	assert(isOk);
	if (isOk)
	{
		for (int i = 0; i<m_pvResult.size(); ++i)
		{
			if (m_pvResult[i] != 'P' && m_pvResult[i] != 'V')
			{
				return false;
			}
		}
		return true;
	}
	return false;
}



void ExpItem::read(FILE *pf)
{
	/*
	1
	F:\drop_box\Dropbox\file_exchange\experiment_1_image\1_1\mid\Image0104.png
	F:\drop_box\Dropbox\file_exchange\experiment_1_image\1_1\mid\Image0104.png
	18
	717 556 P
	975 565 P
	1222 713 P
	1404 1017 P
	1023 1012 P
	850 914 P
	559 969 P
	637 808 P
	1170 625 V
	1273 551 V
	782 733 V
	797 636 V
	611 674 V
	954 844 V
	1077 757 V
	1167 970 V
	1332 924 V
	1202 1051 V
	*/
	char line[512] = {NULL};
	int num = 0;
	num = fscanf(pf, "%d", &m_idx);
	assert(num == 1);

	//left and right image name
	num = fscanf(pf, "%s", line);
	assert(num == 1);
	m_leftName = string(line);
	memset(line, 0, sizeof(line));	

	num = fscanf(pf, "%s", line);
	assert(num == 1);
	m_rightName = string(line);
	memset(line, 0, sizeof(line));

	Mat image = cv::imread(m_leftName, 1);
	assert(!image.empty());

	image = cv::imread(m_rightName, 1);
	assert(!image.empty());

	//points numbers
	int pts_num = 0;
	num  = fscanf(pf, "%d", &pts_num);
	assert(num == 1);	

	for (int i = 0; i<pts_num; ++i)
	{
		int a, b;
		char c;
		num = fscanf(pf, "%d %d %c", &a, &b, &c);
		assert(num == 3);
		m_pts.push_back(cv::Point(a, b));
		m_gt.push_back(c);
	}
	m_pvResult.resize(m_pts.size());

	m_isStereo = (m_leftName != m_rightName);

	_cprintf("Warning: the folder must contain x_1, x_2, x_3, x_4.\n");
	int render_type = 0;
	num = 0;
	for (int i = m_leftName.size()-1; i>=0; --i)
	{
		if (m_leftName[i] >= '0' && m_leftName[i] <= '9')
		{
			num++;
			if (num == 5)
			{
				render_type = m_leftName[i] - '0';
				break;
			}
		}
		
	}
	_cprintf("render type = %d\n", render_type);
	assert(render_type >= 1 && render_type <= 4);
	m_isCaustic = (render_type == 1 || render_type == 3);
	m_isSquare  = (render_type == 1 || render_type == 2);

	if (m_isStereo)
	{
		m_type |= TYPE_STEREO;
	}
	if (m_isSquare)
	{
		m_type |= TYPE_SQUARE;
	}
	if (m_isCaustic)
	{
		m_type |= TYPE_CAUSTIC;
	}

//	random();
}

void ExpItem::write(FILE *pf)
{
	assert(pf);

	fprintf(pf, "%s\n", m_leftName.c_str());
	fprintf(pf, "%s\n", m_rightName.c_str());
	fprintf(pf, "stereo %d\n", m_isStereo);
	fprintf(pf, "square %d\n", m_isSquare);
	fprintf(pf, "caustic %d\n", m_isCaustic);
	for (int i = 0; i<m_gt.size(); ++i)
	{
		fprintf(pf, "%c ", m_gt[i]);
	}
	fprintf(pf, "\n");

	for (int i = 0; i<m_pvResult.size(); ++i)
	{
		fprintf(pf, "%c ", m_pvResult[i]);
	}
	fprintf(pf, "\n");

	//correct ratio
	int correct_num = 0;
	for (int i = 0; i<m_gt.size(); ++i)
	{
		if (m_gt[i] == m_pvResult[i])
		{
			correct_num++;
		}
	}
	m_correctRatio = 1.0*correct_num/m_gt.size();
	fprintf(pf, "correct ratio = %f", m_correctRatio);
	fprintf(pf, "\n\n");
}

bool ExpItem::sparseFile(const string &file, vector<ExpItem> &items)
{
	FILE *pf = fopen(file.c_str(), "r");
	assert(pf);

	int n, m;
	int num = 0;
	num = fscanf(pf, "%d", &n);
	assert(num == 1);

	items.clear();
	for (int i = 0; i<n; ++i)
	{
		ExpItem expIt;
		items.push_back(expIt);
		items.back().read(pf);
	}
	fclose(pf);
	return true;
}

bool ExpItem::saveFile(const string &file, vector<ExpItem> &items)
{
	FILE *pf = fopen(file.c_str(), "w");
	assert(pf);

	fprintf(pf, "%d\n", items.size());
	for (int i = 0; i<items.size(); ++i)
	{
		items[i].write(pf);
	}
	

	{
		const int STEREO = 0;
		const int MONO = 1;
		const int SQUARE = 2;
		const int HEXAGON = 3;
		const int CAUSTIC = 4;
		const int NO_CAUSTIC = 5;
		//stereo correct ratio and monocular correct ratio
		float scr[6]  = {0.0};  //single correctness ratio

		for (int i =0; i<items.size(); ++i)
		{
			float r = items[i].m_correctRatio;
			if((items[i].m_type & TYPE_STEREO) > 0)
			{
				scr[STEREO] += r;
			}
			else
			{
				scr[MONO] += r;
			}

			if ((items[i].m_type & TYPE_SQUARE) > 0)
			{
				scr[SQUARE] += r;
			}
			else
			{
				scr[HEXAGON] += r;
			}

			if ((items[i].m_type & TYPE_CAUSTIC) > 0)
			{
				scr[CAUSTIC] += r;
			}
			else
			{
				scr[NO_CAUSTIC] += r;
			}

		}
		for (int i = 0; i<6; ++i)
		{
			scr[i] /= items.size()/2;
		}

		fprintf(pf, "stereo CR = %f\n, mono CR = %f\n", scr[STEREO], scr[MONO]);
		fprintf(pf, "square CR = %f\n, hex CR = %f\n", scr[SQUARE], scr[HEXAGON]);
		fprintf(pf, "caustic CR = %f\n, no caustic CR = %f\n", scr[CAUSTIC], scr[NO_CAUSTIC]);
	}

	

	const int STEREO_SQUARE  = 0;
	const int STEREO_HEXAGON = 1;
	const int MONO_SQUARE    = 2;
	const int MONO_HEXAGON   = 3;
	const int STEREO_CAUSTIC = 4;
	const int STEREO_NO_CAUSTIC = 5;
	const int MONO_CAUSTIC      = 6;
	const int MONO_NO_CAUSTIC   = 7;
	float bicr[8] = {0.0}; //combined statistics
	for (int i = 0; i<items.size(); ++i)
	{
		float r = items[i].m_correctRatio;
		int type = items[i].m_type;
		if ((type & TYPE_STEREO) > 0 && (type & TYPE_SQUARE) > 0)
		{
			bicr[STEREO_SQUARE] += r;
		}
		if ((type & TYPE_STEREO) > 0 && (type & TYPE_SQUARE) == 0)
		{
			bicr[STEREO_HEXAGON] += r;
		}

		if ((type & TYPE_STEREO) == 0 && (type & TYPE_SQUARE) > 0)
		{
			bicr[MONO_SQUARE] += r;
		}
		if ((type & TYPE_STEREO) == 0 && (type & TYPE_SQUARE) == 0)
		{
			bicr[MONO_HEXAGON] += r;
		}

		if ((type & TYPE_STEREO) > 0 && (type & TYPE_CAUSTIC) > 0)
		{
			bicr[STEREO_CAUSTIC] += r;
		}
		if ((type & TYPE_STEREO) > 0 && (type & TYPE_CAUSTIC) == 0)
		{
			bicr[STEREO_NO_CAUSTIC] += r;
		}

		if ((type & TYPE_STEREO) == 0 && (type & TYPE_CAUSTIC) > 0)
		{
			bicr[MONO_CAUSTIC] += r;
		}
		if ((type & TYPE_STEREO) == 0 && (type & TYPE_CAUSTIC) == 0)
		{
			bicr[MONO_NO_CAUSTIC] += r;
		}
	}
	for (int i = 0; i<8; ++i)
	{
		bicr[i] /= items.size()/4;
	}
	fprintf(pf, "\t    stereo      monocular \n");
	fprintf(pf, "square:     %f\t%f\n", bicr[STEREO_SQUARE], bicr[MONO_SQUARE]);
	fprintf(pf, "hexagon:    %f\t%f\n", bicr[STEREO_HEXAGON], bicr[MONO_HEXAGON]);
	fprintf(pf, "caustic:    %f\t%f\n", bicr[STEREO_CAUSTIC], bicr[MONO_CAUSTIC]);
	fprintf(pf, "no caustic: %f\t%f\n\n", bicr[STEREO_NO_CAUSTIC], bicr[MONO_NO_CAUSTIC]);


	fclose(pf);
	return true;
}

/************************************************************************/
/*
	ExpImagePos
*/
/************************************************************************/


ExpImagePos::ExpImagePos(const string &file)
{
	ExpItem::sparseFile(file, m_data);
	m_curIdx = 0;
	randomImagePosition();
}

ExpImagePos::~ExpImagePos()
{

}

void ExpImagePos::getImages(string &fileA, string &fileB)
{
	assert(m_curIdx >= 0 && m_curIdx < m_randomData.size());
	
	int iIdx = m_randomData[m_curIdx].first;
	fileA = m_data[iIdx].m_leftName;
	fileB = m_data[iIdx].m_rightName;
	
}

void ExpImagePos::getCursorPos(int &x, int &y)
{
	assert(m_curIdx >= 0 && m_curIdx < m_randomData.size());
	int iIdx = m_randomData[m_curIdx].first;
	int pIdx = m_randomData[m_curIdx].second;
	x = m_data[iIdx].m_pts[pIdx].x;
	y = m_data[iIdx].m_pts[pIdx].y;
}

void ExpImagePos::getConfiguration(bool &isStereo, bool &isSquare, bool &isCaustic, char &pOrv)
{
	assert(m_curIdx >= 0 && m_curIdx < m_randomData.size());
	int iIdx = m_randomData[m_curIdx].first;
	int pIdx = m_randomData[m_curIdx].second;
	isStereo = m_data[iIdx].m_isStereo;
	isSquare = m_data[iIdx].m_isSquare;
	isCaustic = m_data[iIdx].m_isCaustic;
	pOrv = m_data[iIdx].m_gt[pIdx];
}

bool ExpImagePos::nextPos()
{
	if (m_curIdx >= 0 && m_curIdx < m_randomData.size()-1)
	{
		m_curIdx++;
		return true;
	}
	else
	{
		//m_curIdx = m_randomData.size();
		return false;
	}

}
bool ExpImagePos::recordButton(const char c)
{
	assert(m_curIdx >= 0 && m_curIdx < m_randomData.size());
	int iIdx = m_randomData[m_curIdx].first;
	int pIdx = m_randomData[m_curIdx].second;
	m_data[iIdx].m_pvResult[pIdx] = c;
	return true;
}
void ExpImagePos::randomImagePosition()
{
	//init image and position index
	for (int i = 0; i<m_data.size(); ++i)
	{
		for(int j = 0; j<m_data[i].m_pts.size(); ++j)
		{
			m_randomData.push_back(std::make_pair<int, int>(i, j));
		}
		if (m_data[i].m_pts.size() != 4)
		{
			_cprintf("%s\n", m_data[i].m_leftName.c_str());
		}
				
	}
	
	//randomly swap
	for (int i = 0; i<m_randomData.size(); ++i)
	{
		int j = randRange(i, m_randomData.size()-1);
		std::swap(m_randomData[i], m_randomData[j]);
	}

	//make sure no exact same image and points
	for (int i = 1; i<m_randomData.size(); ++i)
	{
		int pre_i = m_randomData[i-1].first;
		int pre_j = m_randomData[i-1].second;
		if(pre_i == m_randomData[i].first && pre_j == m_randomData[i].second)
		{
			int k = rand()%m_randomData.size();
			std::swap(m_randomData[i], m_randomData[k]);
		}
	}
}

void ExpImagePos::saveResult(const string &file)
{
	assert(checkResult() == true);
	ExpItem::saveFile(file, m_data);
	_cprintf("write result to file %s\n", file.c_str());
}

void ExpImagePos::GetRestNumber(int &totalNum, int &restNum)
{
	totalNum = m_randomData.size();
	restNum = totalNum - m_curIdx;
}

bool ExpImagePos::checkResult()
{
	for (int i = 0; i<m_data.size(); ++i)
	{
		if (m_data[i].checkResult() == false)
		{
			return false;
		}
	}
	return true;
}



